<template>
  <el-upload
    ref="fileUpload"
    v-model:file-list="fileList"
    :multiple="multiple"
    :action="uploadFileUrl"
    :headers="headers"
    :before-upload="handleBeforeUpload as any"
    :limit="limit"
    :on-error="handleUploadError"
    :on-exceed="handleExceed"
    :on-success="handleUploadSuccess"
    :on-remove="handleRemove"
    :on-change="handleChange"
    :show-file-list="showFileList"
    :data="params"
    :disabled="uploadDisabled"
    :name="binaryName"
    :auto-upload="autoUpload"
    :list-type="listType as any"
    :on-preview="handlePictureCardPreview"
    :class="listType !== 'text' && fileList.length === limit ? 'hideUpload' : ''"
  >
    <template #default>
      <slot v-if="isSlot"></slot>
      <el-button
        v-if="!isSlot"
        :type="buttonType as any"
        :icon="buttonIcon"
        :disabled="disabled"
        :loading="loading"
        :style="{ width: `${buttonWidth}px` }"
        @click="emits('click')"
        :link="buttonLink"
      >
        {{ uploadFileButtonName }}
      </el-button>
    </template>
    <template #tip>
      <el-button v-if="tempVisible" type="danger" link size="small" @click="handDownloadTemp"
        >（没有模板？点我下载模板）
      </el-button>
    </template>
  </el-upload>
</template>

<script setup lang="ts">
import {
  type UploadProps,
  type UploadFile,
  type UploadFiles,
  type UploadRawFile,
  ElLoading,
  ElMessage
} from 'element-plus'
import { useUserStoreWithOut } from '@/store/modules/user'
import { createImageViewer } from '@/components/ImageViewer'
import { saveAs } from 'file-saver'
import { useI18n } from 'vue-i18n'

interface Props {
  url: string
  limit?: number | undefined
  fileSize?: number | undefined
  multiple?: boolean | undefined
  uploadFileButtonName?: string
  fileType?: string[]
  params?: any
  disabled?: boolean | undefined
  uploadDisabled?: boolean | undefined
  autoUpload?: boolean
  showFileList?: boolean
  buttonWidth?: string | number
  buttonLink?: boolean
  buttonIcon?: string
  buttonType?: string
  customErrorAlert?: boolean
  binaryName?: string
  isSlot?: boolean
  fullLoading?: boolean
  tokenKey?: string
  listType?: string
  cover?: boolean
  uploadSuccessRes?: () => void
  handleRemoveFile?: () => void
  fileListMap?: any[]
  downloadFile?: (url: string) => void
  tempVisible?: boolean
  downloadTempData?: { url: string; downloadFormat: string } | undefined
}

const props = withDefaults(defineProps<Props>(), {
  url: '',
  limit: 2,
  fileSize: 15,
  buttonWidth: 76,
  buttonLink: false,
  buttonIcon: '',
  buttonType: 'primary',
  binaryName: 'file',
  multiple: false,
  disabled: false,
  uploadDisabled: false,
  fileType: () => ['png', 'jpg', 'jpeg', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'pdf'],
  uploadFileButtonName: 'upload',
  params: () => ({}),
  autoUpload: true,
  showFileList: false,
  customErrorAlert: false,
  isSlot: false,
  fullLoading: false,
  listType: 'text',
  tokenKey: 'Authorization',
  tempVisible: false,
  cover: false,
  downloadTempData: () => ({ url: '', downloadFormat: '' }),
  // @ts-ignore
  uploadSuccessRes: () => {},
  // @ts-ignore
  handleRemoveFile: () => {},
  fileListMap: () => [],
  downloadFile: () => {}
})

const emits = defineEmits([
  'uploadSuccessRes',
  'handClickUpload',
  'update:file',
  'handlePictureCardPreview',
  'click',
  'handleRemoveFile',
  'uploadError'
])
const {
  url,
  limit,
  multiple,
  fileSize,
  fileType,
  disabled,
  uploadDisabled,
  uploadFileButtonName,
  params,
  autoUpload,
  showFileList,
  buttonWidth,
  buttonLink,
  buttonIcon,
  buttonType,
  customErrorAlert,
  binaryName,
  isSlot,
  listType,
  tokenKey,
  uploadSuccessRes,
  handleRemoveFile,
  fileListMap,
  downloadFile,
  fullLoading,
  tempVisible,
  downloadTempData,
  cover
} = toRefs(props)

const { t } = useI18n()

const userStore = useUserStoreWithOut()

const uploadFileUrl = computed(() => `${import.meta.env.VITE_API_BASE_PATH}${url.value}`)
const headers = computed(() => ({
  [tokenKey.value ?? 'Authorization']: userStore.getToken ?? ''
}))

const fileUpload = ref()
const fileList = ref<any[]>([])
const loading = ref<boolean>(false)
const fullLoadingInstance = ref<Record<string, any>>({})
const fileId = ref<number>(1)

watch(
  fileListMap,
  () => {
    if (showFileList.value && fileListMap.value) fileList.value = fileListMap.value
  },
  { immediate: true }
)

const handDownloadTemp = () => {
  if (tempVisible.value) {
    saveAs(downloadTempData.value.url, downloadTempData.value.downloadFormat)
  }
}

// 上传前校检格式和大小
const handleBeforeUpload: UploadProps['beforeUpload'] = (file: any) => {
  if (fullLoading.value) {
    fullLoadingInstance.value = ElLoading.service({
      text: '正在上传文件，请稍候',
      background: 'rgba(0, 0, 0, 0.7)'
    })
  }
  loading.value = true
  // 校检文件类型
  if (fileType.value.length) {
    const fileName = file.name.split('.')
    const fileExt = fileName[fileName.length - 1]
    const isTypeOk = fileType.value.indexOf(fileExt) >= 0
    if (!isTypeOk) {
      ElMessage.warning({
        message: `文件格式不正确, 请上传${fileType.value.join('/')}格式文件!`
      })
      loading.value = false
      if (fullLoading.value) fullLoadingInstance.value.close()
      return false
    }
  }
  // 校检文件大小
  if (fileSize.value) {
    const isLt = file.size / 1024 / 1024 < fileSize.value
    if (!isLt) {
      ElMessage.error({
        message: `上传文件大小不能超过 ${fileSize.value} MB!`
      })
      loading.value = false
      if (fullLoading.value) fullLoadingInstance.value.close()
      return false
    }
  }
  return true
}
// 文件个数超出
const handleExceed: UploadProps['onExceed'] = (files) => {
  loading.value = false
  if (fullLoading.value) fullLoadingInstance.value.close()

  if (cover.value) {
    fileUpload.value!.clearFiles()
    const file = files[0] as UploadRawFile
    const genFileId = () => Date.now() + fileId.value++
    file.uid = genFileId()
    fileUpload.value!.handleStart(file)
    if (autoUpload.value) {
      fileUpload.value!.submit()
    }
  } else {
    ElMessage.error({
      message: t('common.message.uploadExceed', { limit: limit.value })
    })
  }
}
// 上传失败
const handleUploadError: UploadProps['onError'] = (error: any) => {
  loading.value = false
  if (fullLoading.value) fullLoadingInstance.value.close()
  if (!customErrorAlert.value) {
    ElMessage.error({
      message: t('common.message.uploadError'),
      duration: 4000
    })
  } else {
    ElMessage.error({
      message: t('common.message.uploadError') + `：${JSON.parse(error.message).message}`,
      duration: 4000
    })
  }
}
// 上传成功回调
const handleUploadSuccess: UploadProps['onSuccess'] = (res, uploadFile: UploadFile) => {
  if (res.code == 0 || res.code == 200) {
    if (!showFileList.value) fileList.value = []
    loading.value = false
    if (fullLoading.value) fullLoadingInstance.value.close()
    if (!customErrorAlert.value) {
      ElMessage.success({
        message: t('common.message.uploadSuccess')
      })
    }
    // @ts-ignore
    if (uploadSuccessRes.value) uploadSuccessRes.value({ res: res.data, fileList: fileList.value })
    emits('uploadSuccessRes', { ...res.data, uploadFile })
  } else {
    if (!showFileList.value) fileList.value = []
    loading.value = false
    if (fullLoading.value) fullLoadingInstance.value.close()
    if (!customErrorAlert.value) {
      ElMessage.error({
        message: res.msg || res.message
      })
    }
    emits('uploadError', res.msg || res.message)
  }
}
const handleChange: UploadProps['onChange'] = (
  // @ts-ignore
  uploadFile: UploadFile,
  uploadFiles: UploadFiles
) => {
  if (!autoUpload.value) {
    const Files = uploadFiles.map((item: Record<string, any>) => item.raw)
    if (multiple.value) {
      emits('update:file', Files)
    } else {
      emits('update:file', Files[0])
    }
  }
}

const handleRemove: UploadProps['onRemove'] = (
  // @ts-ignore
  uploadFile: UploadFile,
  uploadFiles: UploadFiles
) => {
  if (!autoUpload.value) {
    const Files = uploadFiles.map((item: Record<string, any>) => item.raw)
    if (multiple.value) {
      emits('update:file', Files)
    } else {
      emits('update:file', Files)
    }
  }
  emits('handleRemoveFile', uploadFile)
  // @ts-ignore
  if (handleRemoveFile.value) handleRemoveFile.value(fileList.value)
}

const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
  const urlList = fileList.value.map((item: any) =>
    item.response ? item.response.data.url : item.url
  )
  const index = fileList.value.findIndex((item: any) => item.uid === uploadFile.uid)
  const file = fileList.value.find((item: any) => item.uid === uploadFile.uid) || {}
  const url = file?.response?.data?.url ?? file.url
  downloadFile.value(url)
  emits('handlePictureCardPreview', url)
  if (listType.value === 'text') return
  // @ts-ignore
  createImageViewer({
    urlList,
    initialIndex: index
  })
}
defineExpose({ fileList })
</script>

<style lang="less" scoped>
.hideUpload :deep(.el-upload--picture-card) {
  display: none !important;
}
</style>
